#include <vector>
#include <string>
#include <unordered_set>
#include <queue>
#include <memory>
#include <unordered_map>
#include <functional>
#include "net/zmqproxy.h"
#include "utils/timer.h"
#include "zmq.h"

using namespace std;

class Client
{
    public:
    string ip;
    Client(string ip):ip(ip)
    {}
};

typedef shared_ptr<Client> client_sptr_t;

struct Entry
{
    string ip;
    weak_ptr<Client> client_wptr;
    function<void(string)> NotifyCounterRemove;

    Entry(string ip): ip(ip)
    {}

    ~Entry()
    {
        // 通知计数器清除当前包裹的信息
        NotifyCounterRemove(ip);
    }
};
typedef shared_ptr<Entry> entry_sptr_t;
typedef weak_ptr<Entry> entry_wptr_t;

class EntryHash
{
public:
    std::size_t operator() (const entry_sptr_t& c) const
    {
        return std::hash<std::string>()(c->ip);
    }
};

typedef unordered_set<entry_sptr_t, EntryHash> Bucket;

class TimeWheel
{
    private:
    int maxTick;
    queue<Bucket> timewheel;

    public:
    void init(int maxTick) {this->maxTick = maxTick;}
    void tick()
    {
        timewheel.push(Bucket());
        if(timewheel.size() > maxTick) 
        {
            timewheel.pop();
        }
    }
    void join(entry_sptr_t client)
    {
        timewheel.back().insert(client);
    }
};

class Context
{
    unordered_map<string, client_sptr_t> client_map;
    unordered_map<string, entry_wptr_t> entry_map;
    public:
        void setContext(entry_wptr_t& client_wptr)
        {
            auto client_sptr = client_wptr.lock();
            if(client_sptr)
            {
                entry_map[client_sptr->ip] = client_wptr;
            }
        }

        client_sptr_t getConn(string& ip)
        {
            if(client_map.find(ip) != client_map.end())
            {
                return client_map[ip];
            }
            else
            {
                client_map[ip] = make_shared<Client>(ip);
                return client_map[ip];
            }
        }

        entry_sptr_t getEntry(client_sptr_t client_sptr)
        {
            if(entry_map.find(client_sptr->ip) != entry_map.end())
            {
                return entry_sptr_t(entry_map[client_sptr->ip]);
            }
            else
            {
                entry_sptr_t entry_sptr = make_shared<Entry>(client_sptr->ip);
                entry_sptr->NotifyCounterRemove = std::bind(&Context::clearConn, this, std::placeholders::_1);
                entry_map[client_sptr->ip] = weak_ptr<Entry>(entry_sptr);
                return entry_sptr;
            }
        }

        void clearConn(string ip)
        {
            client_map.erase(ip);
            entry_map.erase(ip);
        }

        int getClientCount() {return client_map.size();}

        void showAll()
        {
            for ( const auto& item : client_map)
            {
                printf("%s ", item.first.c_str());
            }
            printf("\n");

        }
};



int main(int argc, char const *argv[])
{
    // 链接客户端，一旦收到消息将客户端包裹成client放入timewheel
    ZmqPair listener(9776);
    listener.CreateRecevier();
    int timeFd = CreateTimeoutfd(2);
    zmq_pollitem_t items[] = {
        {listener.getSocket(), 0, ZMQ_POLLIN, 0},
        {nullptr, timeFd, ZMQ_POLLIN, 0},
    };
    TimeWheel wheel;
    wheel.init(3);

    Context context;

    while(1)
    {   
        int num = zmq_poll(items, 2, -1);
        if(num == -1)
        {
            continue;
        }
        if(items[0].revents & ZMQ_POLLIN)
        {   
            string sMsg;
            int len = listener.recv(sMsg);
            auto client_sptr = context.getConn(sMsg);
            auto entry_sptr = context.getEntry(client_sptr);

            wheel.join(entry_sptr);
        }

        if(items[1].revents & ZMQ_POLLIN)
        {
            wheel.tick();
            printf("client count: %d\n", context.getClientCount());
            context.showAll();
            SetTimeout(timeFd, 2);
        }
    }

    return 0;
}
